<template>
  <div ref="outerWrapper" :class="wrapperClasses">
    <div v-if="isHorizontal" :class="`${prefix}-horizontal`">
      <div
        :style="{ right: `${anotherOffset}%` }"
        :class="[`${prefix}-pane`, 'left-pane']"
      >
        <slot name="left" />
      </div>
      <div
        :class="`${prefix}-trigger-con`"
        :style="{ left: `${offset}%` }"
        @mousedown="handleMousedown"
      >
        <slot name="trigger">
          <trigger mode="vertical" />
        </slot>
      </div>
      <div
        :style="{ left: `${offset}%` }"
        :class="[`${prefix}-pane`, 'right-pane']"
      >
        <slot name="right" />
      </div>
    </div>
    <div v-else :class="`${prefix}-vertical`">
      <div
        :style="{ bottom: `${anotherOffset}%` }"
        :class="[`${prefix}-pane`, 'top-pane']"
      >
        <slot name="top" />
      </div>
      <div
        :class="`${prefix}-trigger-con`"
        :style="{ top: `${offset}%` }"
        @mousedown="handleMousedown"
      >
        <slot name="trigger">
          <trigger mode="horizontal" />
        </slot>
      </div>
      <div
        :style="{ top: `${offset}%` }"
        :class="[`${prefix}-pane`, 'bottom-pane']"
      >
        <slot name="bottom" />
      </div>
    </div>
  </div>
</template>

<script>
import { oneOf, on, off } from "@/libs/tools";
import Trigger from "./trigger.vue";
export default {
  name: "SplitPane",
  components: {
    Trigger,
  },
  props: {
    value: {
      type: [Number, String],
      default: 0.5,
    },
    mode: {
      validator(value) {
        return oneOf(value, ["horizontal", "vertical"]);
      },
      default: "horizontal",
    },
    min: {
      type: [Number, String],
      default: "40px",
    },
    max: {
      type: [Number, String],
      default: "40px",
    },
  },
  /**
   * Events
   * @on-move-start
   * @on-moving 返回值：事件对象，但是在事件对象中加入了两个参数：atMin(当前是否在最小值处), atMax(当前是否在最大值处)
   * @on-move-end
   */
  data() {
    return {
      prefix: "ivu-split",
      offset: 0,
      oldOffset: 0,
      isMoving: false,
    };
  },
  computed: {
    wrapperClasses() {
      return [`${this.prefix}-wrapper`, this.isMoving ? "no-select" : ""];
    },
    isHorizontal() {
      return this.mode === "horizontal";
    },
    anotherOffset() {
      return 100 - this.offset;
    },
    valueIsPx() {
      return typeof this.value === "string";
    },
    offsetSize() {
      return this.isHorizontal ? "offsetWidth" : "offsetHeight";
    },
    computedMin() {
      return this.getComputedThresholdValue("min");
    },
    computedMax() {
      return this.getComputedThresholdValue("max");
    },
  },
  methods: {
    px2percent(numerator, denominator) {
      return parseFloat(numerator) / parseFloat(denominator);
    },
    getComputedThresholdValue(type) {
      let size = this.$refs.outerWrapper[this.offsetSize];
      if (this.valueIsPx)
        return typeof this[type] === "string" ? this[type] : size * this[type];
      else
        return typeof this[type] === "string"
          ? this.px2percent(this[type], size)
          : this[type];
    },
    getMin(value1, value2) {
      if (this.valueIsPx)
        return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;
      else return Math.min(value1, value2);
    },
    getMax(value1, value2) {
      if (this.valueIsPx)
        return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
      else return Math.max(value1, value2);
    },
    getAnotherOffset(value) {
      let res = 0;
      if (this.valueIsPx)
        res = `${
          this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)
        }px`;
      else res = 1 - value;
      return res;
    },
    handleMove(e) {
      let pageOffset = this.isHorizontal ? e.pageX : e.pageY;
      let offset = pageOffset - this.initOffset;
      let outerWidth = this.$refs.outerWrapper[this.offsetSize];
      let value = this.valueIsPx
        ? `${parseFloat(this.oldOffset) + offset}px`
        : this.px2percent(outerWidth * this.oldOffset + offset, outerWidth);
      let anotherValue = this.getAnotherOffset(value);
      if (parseFloat(value) <= parseFloat(this.computedMin))
        value = this.getMax(value, this.computedMin);
      if (parseFloat(anotherValue) <= parseFloat(this.computedMax))
        value = this.getAnotherOffset(
          this.getMax(anotherValue, this.computedMax)
        );
      e.atMin = this.value === this.computedMin;
      e.atMax = this.valueIsPx
        ? this.getAnotherOffset(this.value) === this.computedMax
        : this.getAnotherOffset(this.value).toFixed(5) ===
          this.computedMax.toFixed(5);
      this.$emit("input", value);
      this.$emit("on-moving", e);
    },
    handleUp() {
      this.isMoving = false;
      off(document, "mousemove", this.handleMove);
      off(document, "mouseup", this.handleUp);
      this.$emit("on-move-end");
    },
    handleMousedown(e) {
      this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
      this.oldOffset = this.value;
      this.isMoving = true;
      on(document, "mousemove", this.handleMove);
      on(document, "mouseup", this.handleUp);
      this.$emit("on-move-start");
    },
  },
  watch: {
    value() {
      this.offset =
        ((this.valueIsPx
          ? this.px2percent(
              this.value,
              this.$refs.outerWrapper[this.offsetSize]
            )
          : this.value) *
          10000) /
        100;
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.offset =
        ((this.valueIsPx
          ? this.px2percent(
              this.value,
              this.$refs.outerWrapper[this.offsetSize]
            )
          : this.value) *
          10000) /
        100;
    });
  },
};
</script>

<style lang="less">
@import "./index.less";
</style>